package gipfProject.games.zertz.data;

import java.util.Iterator;
import java.util.List;
import java.util.Vector;

public class ZertzData {
	public static final int placeMarble = 0, removeRing = 1, captureMarble = 2;
	public static final int boardCols = 7, boardRows = 13;
	public static final int playerWhite = 0, playerBlack = 1;
	public static final String[] playerName = {"White Player", "Black Player"};

	private boolean isGameRunning;
	
	public PieceData[][] board;
	public int moveType;
	public String errorMessage;

	private int currentPlayer;
	private Location selectedPiece;
	private int placeType;
		
	private int goal[];
	private int pool[];
	private int poolsize[];
	private int score[][];
	
	public ZertzData() {	
		// Initialize the game pieces
		board = new PieceData[boardCols][boardRows];

		for (int col = 0; col < boardCols; col++) {
			for (int row = 0; row < boardRows; row++) {
				board[col][row] = new PieceData();
			}
		}
		
		//Initialize the scores and pool
		goal = new int[4];
		pool = new int[3];
		poolsize = new int[3];
		score = new int[2][3];
		
		newGame();
	}
	
	public void newGame(){
		//Layout the board
		makeBoard();
		
		//First move is to place a marble
		moveType = placeMarble;
		
		//First player is white
		currentPlayer = playerWhite;
		
		//Set default scores
		setScores();
		
		//Set default goals
		setGoals();
		
		//Default to placing white marbles
		setPlaceType(PieceData.WHITE);
		
		//Ensure no piece is pre-selected
		clearSelectedPiece();	
		
		//Clear the error message
		errorMessage = "";
		
		//Set the game to be running
		isGameRunning = true;
	}
	
	private void setScores() {
		//Set the scores to zero
		for (int i = 0; i < 3 ; i++){
			score[0][i] = 0;
			score[1][i] = 0;
		}
		
		//Set the pool to it's initial sizes
		pool[PieceData.WHITE] = poolsize[PieceData.WHITE] = 10;
		pool[PieceData.GRAY]  = poolsize[PieceData.GRAY ] =  8;
		pool[PieceData.BLACK] = poolsize[PieceData.BLACK] =  6;
	}
	
	private void setGoals() {
		goal[PieceData.WHITE] = 6;
		goal[PieceData.GRAY]  = 5;
		goal[PieceData.BLACK] = 4;
		goal[PieceData.ALL]   = 3;
 	}

	/**
	 * Sets the board to the initial configuration
	 */
	private void makeBoard() {

		for (int col = 0; col < boardCols; col++) {
			for (int row = 0; row < boardRows; row++) {
				if ((row + 1) % 2 == col % 2) {
					board[col][row].setToEmpty();

					//FIXME: Testing Code
					if (false)
					{
						if (Math.random() > 0.2){
							board[col][row].setToEmpty();
						}else{
							board[col][row].setType(PieceData.WHITE);
						}
					}
				}
			}
		}

		// Remove the eight remaining locations that are not valid tiles
		board[0][1].setToInvalid();
		board[1][0].setToInvalid();
		board[5][0].setToInvalid();
		board[6][1].setToInvalid();
		board[0][11].setToInvalid();
		board[1][12].setToInvalid();
		board[5][12].setToInvalid();
		board[6][11].setToInvalid();
	}

	/**
	 * Handles all End Of Move maintenance.
	 * Currently just alternates which moveType the next move will be
	 */

	public void endMove() {
		int previousMove = moveType;
		
		RuleData rules = new RuleData(this);
		IsolationRules checkIsolation = new IsolationRules(rules,this);
		
		//Check if any marbles are being removed due to isolation
		checkIsolation.runLife();
		
		//If we placed a marble, then we have to remove a ring as well
		if (previousMove == placeMarble) {
			if (rules.canAnyoneRemove()){
				moveType = removeRing;
			}else{
				endTurn();
			}	
		//Otherwise we move to the next turn
		//(i.e. captures, cannot remove ring, removed ring)
		}
		else
		{
			endTurn();
		}
	}
	
	private void endTurn() {
		RuleData rules = new RuleData(this);
		
		//If this was a capture AND there are more captures available
		// Then the player must make those before the turn ends
		if (moveType == captureMarble && rules.canAnyoneCapture()){
			return;
		}

		//Change what player it is
		currentPlayer = (currentPlayer+1) % 2;

		//And set the next move
		if (rules.canAnyoneCapture()){
			moveType = captureMarble;
		}else{
			moveType = placeMarble;
		}
		
		//Ensure the new player can make a move
		if (!rules.anyValidMoves()){
			currentPlayer = (currentPlayer+1) % 2;
			if (!rules.anyValidMoves()){
				errorMessage = "Game ended: No available moves";
				endGame();
			}else{
				errorMessage = "Turn skipped for no valid moves";
			}
		}
		
		//Check if anyone has won
		checkVictoryConditions();
		
	}//End of endTurn()

	private void checkVictoryConditions()
	{
		//Check if any player has reached the capture goals
		for(int player = 0; player < 2 ; player++){
			//Check the goals for captures of a specific type
			for (int marbleType = 0; marbleType < 3 ; marbleType++){
				if (score[player][marbleType] >= goal[marbleType]){
					victory(player);
					return;
				}				
			}
			
			//Check the goal for capturing X (default: 3) of each type
			if (score[player][PieceData.WHITE] == goal[PieceData.ALL] &&
				score[player][PieceData.GRAY]  == goal[PieceData.ALL] &&
				score[player][PieceData.BLACK] == goal[PieceData.ALL]
			)
			{
				victory(player);
				return;
			}
		}
	}
	
	public void resign()
	{
		defeat(getCurrentPlayer());
	}
	
	private void endGame()
	{
		isGameRunning = false;
	}
	
	private void victory(int player){
		//End the game
		endGame();
		
		//Announce victory
		errorMessage = "The game was won by " + playerName[player] + "!";
	}
	
	private void defeat(int player){
		//The other player has won
		player = (player+1) % 2;
		victory(player);
	}
	

	/**
	 * Place a marble of the specified type at the specified location
	 * 
	 * @param col
	 * @param row
	 * @param type
	 */
	public void placeMarble(int col, int row) {
		board[col][row].setType(getPlaceType());
		
		//Check if there are any marbles available in the pool
		if (pool[getPlaceType()] > 0){
			//If so, deduct from there
			pool[getPlaceType()]--;
		}else{
			//Otherwise deduct from previous captures
			score[getCurrentPlayer()][getPlaceType()]--;
		}
	}

	/**
	 * Removes the ring at the specified location
	 * 
	 * @param col
	 * @param row
	 */
	public void removeRing(int col, int row) {
		board[col][row].setToInvalid();
	}
	
	public void captureMarble(int startCol, int startRow, int endCol, int endRow){
		//Calculate the intermediate location
		Location oldLocation = new Location (startCol,startRow);
		Location newLocation = new Location (endCol,  endRow  );
		int direction = oldLocation.directionTo(newLocation);
		Location midLocation = oldLocation.moveDirection(direction,1) ;
		
		//Put the moving marble at it's new location
		board[endCol][endRow].setType(board[startCol][startRow].getType());
		
		//Increment the score
		int marbleType = board[midLocation.x][midLocation.y].getType();
		score[getCurrentPlayer()][marbleType]++;
		
		//Remove the captured marble
		board[midLocation.x][midLocation.y].setToEmpty();

		//Last, remove the marble from it's original position
		board[startCol][startRow].setToEmpty();
		
		//Clear the selection
		clearSelectedPiece();
	}
	
	/**
	 * This is used for capturing marbles due to Isolation
	 * @param col
	 * @param row
	 */
	public void isolationCapture(int col, int row){
		//Increment the score
		int marbleType = board[col][row].getType();
		score[getCurrentPlayer()][marbleType]++;
		
		//And remove both the marble and the ring
		board[col][row].setToInvalid();
	}

	/**
	 * Sets the error message contents
	 * 
	 * Must run ZertzUI.refreshMessage() to
	 * actually refresh the display
	 * 
	 * @param input
	 */
	public void setErrorMessage(String input) {
		errorMessage = input;
	}

	/**
	 * Clears the error message contents Must run ZertzUI.refreshMessage() to
	 * actually refresh the display
	 */
	public void clearErrorMessage() {
		errorMessage = "";
	}

	/**
	 * Returns the currently-stored error message
	 * 
	 * @return
	 */
	public String getErrorMessage() {
		return errorMessage;
	}
	
	public int getCurrentPlayer(){
		return currentPlayer;
	}
	
	public String getCurrentPlayerName(){
		return playerName[currentPlayer];
	}
	
	public String getName(int index){
		return playerName[index];
	}
	
	public String getCurrentMoveName(){
		if (moveType == placeMarble) {
			return "Place a marble";
		}
		else if (moveType == removeRing)
		{
			return "Remove a ring";
		}
		else if (moveType == captureMarble)
		{
			return "Capture a marble";
		}
		else
		{
			//This should never occur
			System.err.println("ZertzData: getCurrentMoveName() could not find a valid moveType");
			return "N/A";
		}
	}
	
	public boolean isWhitePlayer(){
		return (currentPlayer == playerWhite);
	}
	
	public Location getSelectedPiece(){
		return selectedPiece;
	}
	
	public void clearSelectedPiece(){
		if (selectedPiece != null){
			board[selectedPiece.x][selectedPiece.y].clearHighlight();
			selectedPiece = null;
		}
	}
	
	public void selectPiece(int col, int row){
		board[col][row].setHighlight();
		
		Location selectedLocation = new Location(col,row);
		selectedPiece = selectedLocation;
	}
	
	public int getPlaceType(){
		return placeType;
	}
	
	public void setPlaceType(int input){
		placeType = input;
	}

	public int getGoal(int index){
		return goal[index];
	}
	
	public int getPool(int index){
		return pool[index];
	}
	
	public int getPoolSize(int index){
		return poolsize[index];
	}
	
	public int getScore(int player, int index){
		return score[player][index];
	}
	
	public boolean isPoolEmpty(){
		return ( getPool(0) <= 0 && getPool(1) <= 0 && getPool(2) <= 0 );
	}
	
	public boolean isGameRunning(){
		return isGameRunning;
	}
	
	public PieceData[][] getBoard(){
		return board;
	}
	
	public PieceData getPiece(int x, int y){
		return board[x][y];
	}
	
	private List<Location> listNeighbors(Location startLocation, boolean skipValid){
		List<Location> listNeighbors = new Vector<Location>();
		RuleData rules = new RuleData(this);
		
		//If this isn't a valid location, return a null list
		if(!rules.validLocation(startLocation)){
			return null;
		}
		
		//For each adjacent direction
		for (int direction = 1; direction <= 6 ; direction++){
			//Find the adjacent location
			Location testLocation = startLocation.moveDirection(direction,1);
			
			//Check that this is a legal position on the board
			if (rules.validLocation(testLocation)){
				//Check that this is a valid piece OR that we are skipping validation
				if (board[testLocation.x][testLocation.y].isValid() || skipValid){
					//Add it to the list
					listNeighbors.add(testLocation);
				}
			}
		}
		
		//Return the assembled list
		return listNeighbors;
	}
	
	public List<Location> listAllNeighbors(Location startLocation)
		{ return listNeighbors(startLocation,true); }
	
	public List<Location> listValidNeighbors(Location startLocation)
		{ return listNeighbors(startLocation,false); }
	
	public Boolean canPieceCapture(int col, int row){
		RuleData rules = new RuleData(this);
		return rules.canPieceCapture(col, row);
	}
	
	public Boolean canCompleteCapture(int col, int row){
		RuleData rules = new RuleData(this);
		return rules.canCapture(selectedPiece.x, selectedPiece.y, col, row);
	}
}
